package com.lau.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Method;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


/**
 * @author:Lau
 * @create: 2022-12-12 11:19
 * @Description: 多线程
 */
@Configuration
@EnableAsync
@Slf4j
public class ThreadConfig {

    // 核心线程池大小
    private int executorcorePoolSize = 100;

    // 核心线程池大小
    private int corePoolSize = 50;

    // 最大可创建的线程数
    private int maxPoolSize = 200;

    // 队列最大长度
    private int queueCapacity = 100;

    // 线程池维护线程所允许的空闲时间
    private int keepAliveSeconds = 60;

    /**
     * ThreadPoolTaskExecutor的处理流程
     * 当池子大小小于corePoolSize，就新建线程，并处理请求
     * 当池子大小等于corePoolSize，把请求放入workQueue中，池子里的空闲线程就去workQueue中取任务并处理
     * 当workQueue放不下任务时，就新建线程放入线程池，并处理请求，如果池子大小撑到了maximumPoolSize，就用RejectedExecutionHandler来做拒绝处理
     * 当池子的线程数大于corePoolSize时，多余的线程会等待keepAliveTime长时间，如果无请求可处理就自行销毁
     *
     * @return
     */
    @Bean("async")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor tp = new ThreadPoolTaskExecutor();
        //设置核心线程数
        tp.setCorePoolSize(corePoolSize);
        //设置最大线程数
        tp.setMaxPoolSize(maxPoolSize);
        //线程使用的缓冲队列
        tp.setQueueCapacity(queueCapacity);
        //设置程序关闭时要等待线程全部执行完
        tp.setWaitForTasksToCompleteOnShutdown(true);
        //设置等待时间，超过等待时间后立即停止
        tp.setAwaitTerminationSeconds(keepAliveSeconds);
        //线程名称前缀
        tp.setThreadNamePrefix("Async-");
        /* 当线程的任务缓存队列已满并且线程池中的线程数量已经达到了最大连接数，如果还有任务来就会采取拒绝策略，
         * 通常有四种策略：
         *ThreadPoolExecutor.AbortPolicy：丢弃任务并抛出异常：RejectedExcutionException异常
         *ThreadPoolExecutor.DiscardPolicy：丢弃任务，但是不抛出异常
         *ThreadPoolExecutor.DiscardOldestPolicy: 丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）
         *ThreadPoolExecutor.CallerRunsPolicy：重试添加当前的任务，自动重复调用execute()方法，直到成功。
         *ThreadPoolExecutor. 扩展重试3次，如果3次都不充公在移除。
         *jmeter 压力测试 1s=500
         * */
        tp.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //初始化线程
        tp.initialize();
        return tp;
    }

    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        log.info("-----------多线程异常handler------------");
        return new SpringAsyncExceptionHandler();
    }

    class SpringAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
        @Override
        public void handleUncaughtException(Throwable ex, Method method, Object... params) {
            log.error("Asyn无返回异常：" + ex.getCause().getMessage() + method.getName() + params);
        }
    }

    /**
     * 执行周期性或定时任务通过ScheduledExecutorService执行的异步任务，最多只能executorcorePoolSize个线程同时执行，不会新增
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService() {
        return new ScheduledThreadPoolExecutor(executorcorePoolSize,
                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
                new ThreadPoolExecutor.CallerRunsPolicy()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                if(t!=null)log.error(t.getMessage());

            }
        };
    }
    /**
     * 有界队列：
     * SynchronousQueue ：一个不存储元素的阻塞队列，每个插入操作必须等到另一个线程调用移除操作，否则插入操作一直处于 阻塞状态，吞吐量通常要高于LinkedBlockingQueue，
     * 静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。
     * ArrayBlockingQueue：一个由数组支持的有界阻塞队列。此队列按 FIFO（先进先出）原则对元素进行排序。一旦创建了这样的缓存区，就不能再增加其容量。
     * 试图向已满队列中放入元素会导致操作受阻塞；试图从空队列中提取元素将导致类似阻塞。
     *
     * 2）无界队列：
     * LinkedBlockingQueue：基于链表结构的无界阻塞队列，它可以指定容量也可以不指定容量（实际上任何无限容量的队列/栈都是有容量的，这个容量就是Integer.MAX_VALUE）
     * PriorityBlockingQueue：是一个按照优先级进行内部元素排序的无界阻塞队列。队列中的元素必须实现 Comparable 接口，这样才能通过实现compareTo()方法进行排序。
     * 优先级最高的元素将始终排在队列的头部；PriorityBlockingQueue 不会保证优先级一样的元素的排序。
     * 注意：keepAliveTime和maximumPoolSize及BlockingQueue的类型均有关系。如果BlockingQueue是无界的，那么永远不会触发maximumPoolSize，自然keepAliveTime也就没有了意义。
     */
    @Bean(name = "executorService")
    protected ThreadPoolExecutor executorService () {
        return new ThreadPoolExecutor(corePoolSize, maxPoolSize,
                keepAliveSeconds, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(queueCapacity),
                new BasicThreadFactory.Builder().namingPattern("ThreadPool-%d").daemon(true).build(),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }
}
